package com.ctg.itrdc.imageconsole.servcie.user;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;

import javax.annotation.Resource;

import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.stereotype.Component;

import com.ctg.itrdc.imageconsole.data.dao.IDockerMicroUserDao;
import com.ctg.itrdc.imageconsole.data.dto.DmsAuthorityDTO;
import com.ctg.itrdc.imageconsole.data.dto.user.PasswordDTO;
import com.ctg.itrdc.imageconsole.data.dto.user.UserDTO;
import com.ctg.itrdc.imageconsole.data.entity.user.DockerMicroUser;
import com.ctg.itrdc.imageconsole.util.HarborUtil;
import com.ctg.itrdc.imageconsole.util.SysManagerUtil;

@Component("userService")
public class UserService {
	
	
	private static String HARBORADMINTOKEN = null;
		
	@Resource 
	IDockerMicroUserDao microUserDao;
		
	@Resource
	UserServiceAux userServiceAux;
	
	public void addUser(UserDTO userDTO, DmsAuthorityDTO authorityDTO) {
		if(!this.checkPassword(userDTO.getPassword())) 
			throw new RuntimeException("Error : password is invalid");
		
		String[] harbors = ConfigReader.readHarborHarbors();
		
		final String userCode = userDTO.getUserCode();
		final String userName = userDTO.getUserName();
		final String password = userDTO.getPassword();				
		
		DockerMicroUser microUser = microUserDao.selectByCode(userCode);
		if(null != microUser)
			throw new RuntimeException(String.format("Error : user already exists"));
		
		microUser = new DockerMicroUser();
		microUser.setCode(userCode);
		microUser.setName(userName);
		microUser.setPassword(password);					
		microUser.setCreateUserId(authorityDTO.getCreateUserId());
		microUser.setCreateTime(authorityDTO.getCreateTime());		
		microUser.setLastUpdateUserId(authorityDTO.getLastUpdateUserId());
		microUser.setLastUpdateTime(authorityDTO.getLastUpdateTime());		
		Long userId = microUserDao.insert(microUser);
		microUser.setId(userId);
		
		// add user in sysManager		
		Long sysUserId = null;
		try {
			sysUserId = SysManagerUtil.addUser(ConfigReader.readSysManagerEndpoint(), userCode, 
					password, ConfigReader.readSysManagerStaffId(), ConfigReader.readSysManagerSysCode());
		} catch (Exception e) {
			microUser.setAvailable(0);
			microUser.setFailureReason("Failed to add user in sysManager");
			microUserDao.updateSelectiveByPrimaryKey(microUser);
			throw new RuntimeException(e.getMessage());
		}
		
		microUser.setSysUserId(sysUserId);					
			
		String failedHarbors = null;
		Integer available = 1;
		for(String harbor : harbors) {
			try {
				// add user
				HarborUtil.addUser(harbor, this.getHarborAdminToken(), userDTO);
				
				// create project
				HarborUtil.createProject(harbor, this.getToken(userCode, password), userCode, 1);
				
				// get project_id,  use user's token			
				JSONArray projects = HarborUtil.getProjects(harbor, this.getToken(userCode, password), userCode);
				if(null == projects || projects.length() != 1)
					throw new RuntimeException(String.format("Error : project %s not found or duplicated in %s when creating replication policy", userCode, harbor));
				Long projectId = projects.getJSONObject(0).getLong("project_id");
										
				JSONArray targets = HarborUtil.getTargets(harbor, this.getHarborAdminToken(), null);
				if (targets == null) { continue; }
				for(int i = 0; i < targets.length(); i++) {				
					// create policies replication, with replication disabled					
					Long targetId = targets.getJSONObject(i).getLong("id");
					String replicationName = targets.getJSONObject(i).getString("name");	
					HarborUtil.createPoliciesReplication(harbor, this.getHarborAdminToken(), projectId, targetId, replicationName);
					
					// get replication_id -- { project_id, name }
					JSONArray replications = HarborUtil.getPoliciesReplications(harbor, this.getHarborAdminToken(), projectId, replicationName);
					if(null == replications || replications.length() != 1)
						throw new RuntimeException(String.format("Error : policy replication not found or duplicated in %s when enabling it", harbor));
					Long replicationId  = replications.getJSONObject(0).getLong("id");
					
					// enable replication -- {replicationId, enabled}				
					HarborUtil.enableOrDisablePoliciesReplication(harbor, this.getHarborAdminToken(), replicationId, 1);
				}
			} catch (Exception e) {
				failedHarbors = failedHarbors + harbor + ",";
				available = 0;
			}				
		}	
		microUser.setAvailable(available);
		microUser.setFailureReason(failedHarbors);
		microUserDao.updateSelectiveByPrimaryKey(microUser);
		if(null != failedHarbors)
			throw new RuntimeException(String.format("Error : failed to add user in %s", failedHarbors));
				
	}	
	
	
	public void tryToFixUser(Long userId) {
		// get microUser
		DockerMicroUser microUser = microUserDao.selectById(userId);
		if(null == microUser)
			throw new RuntimeException(String.format("Error : user %d not found in db", userId));
		String[] harbors = ConfigReader.readHarborHarbors();
		
		// check and fix in sysManager
		try {
			JSONObject sysUser = SysManagerUtil.getUserByCode(ConfigReader.readSysManagerEndpoint(), microUser.getCode());
			if(null != sysUser) {	// update_password
				SysManagerUtil.updatePassword(ConfigReader.readSysManagerEndpoint(), sysUser.getLong("id"), 
						microUser.getPassword(), ConfigReader.readSysManagerStaffId(), ConfigReader.readSysManagerSysCode());
				microUser.setSysUserId(sysUser.getLong("id"));
			} else {	// add user
				SysManagerUtil.addUser(ConfigReader.readSysManagerEndpoint(), microUser.getCode(), 
						microUser.getPassword(), ConfigReader.readSysManagerStaffId(), ConfigReader.readSysManagerSysCode());
			}
		} catch (Exception e) {
			microUser.setFailureReason("failed to fixed in sysManager");
			microUserDao.updateSelectiveByPrimaryKey(microUser);
		}
		
			
		for(String harbor : harbors) {
			// check and fix user
			JSONArray users = HarborUtil.getUsers(harbor, this.getHarborAdminToken(), microUser.getCode());
			JSONObject user = null;
			for(int i = 0; i < users.length(); i++) {
				user = users.getJSONObject(i);
				if(microUser.getCode().equals(user.getString("username"))) 
					break;			
			}
		//	if()
			
			// check and fix project
			
			// check and fix replication policy
		}
	}
	
	
	// delete
	public void deleteUser(Long userId) {
		DockerMicroUser microUser = new DockerMicroUser();
		microUser.setId(userId);
		microUserDao.deleteByPrimaryKey(microUser);
		// delete user in sysManager
		SysManagerUtil.deleteUser(ConfigReader.readSysManagerEndpoint(), microUser.getSysUserId(), 
				ConfigReader.readSysManagerStaffId(), ConfigReader.readSysManagerSysCode());	
		
	}	
	
	
	// update
	public void updatePassword(Long userId, PasswordDTO passwordDTO, DmsAuthorityDTO authorityDTO) {
		if(!checkPassword(passwordDTO.getNewPassword()))
			throw new RuntimeException(String.format("Error : new password is invalid"));
		DockerMicroUser microUser = microUserDao.selectById(userId);
		if(null == microUser)
			throw new RuntimeException(String.format("Error : user %d not found in db", userId));
		if(!passwordDTO.getOldPassword().equals(microUser.getPassword()))
			throw new RuntimeException(String.format("Error : old password not correct"));		
		
		// start -- update password in db -- 
		microUser.setPassword(passwordDTO.getNewPassword());
		microUser.setLastUpdateUserId(authorityDTO.getLastUpdateUserId());
		microUser.setLastUpdateTime(authorityDTO.getLastUpdateTime());
		microUserDao.updateSelectiveByPrimaryKey(microUser);
		// end -- update password in db -- 
		
		//	 update in auth 
		SysManagerUtil.updatePassword(ConfigReader.readSysManagerEndpoint(), microUser.getSysUserId(), 
				passwordDTO.getNewPassword(), ConfigReader.readSysManagerStaffId(), ConfigReader.readSysManagerSysCode());
		
		//  update in harbor 
		String[] harbors = ConfigReader.readHarborHarbors();
		for(String harbor : harbors) {	
			JSONArray users = HarborUtil.getUsers(harbor, this.getHarborAdminToken(), microUser.getCode());
			Long harborUserId = users.getJSONObject(0).getLong("user_id");
			HarborUtil.updatePasswordByUserId(harbor, this.getToken(microUser.getCode(), passwordDTO.getOldPassword()),
					harborUserId, passwordDTO.getOldPassword(), passwordDTO.getNewPassword());
		}			
	}
	
	
	
	/*
	public boolean userCodeExists(String userCode) {		
		DockerMicroUser microUser = new DockerMicroUser();
		microUser.setCode(userCode);
		List<DockerMicroUser> users = microUserDao.selectByExample(microUser);		
		return !users.isEmpty() && 0 == users.get(0).getDeleted();
	}
	*/
	
	public boolean userCodeExists(String userCode) {		
		DockerMicroUser microUser = new DockerMicroUser();
		microUser.setCode(userCode);
		List<DockerMicroUser> users = microUserDao.selectByExample(microUser);		
		return !users.isEmpty();
	}
	
	private String getHarborAdminToken() {
		if(null == HARBORADMINTOKEN) {
			String adminUsername = ConfigReader.readHarborUsername();
			String adminPassword = ConfigReader.readHarborPassword();	
			HARBORADMINTOKEN = this.getToken(adminUsername, adminPassword);
		}
		return HARBORADMINTOKEN;			
	}

	private String getToken(String username, String password) {
		byte[] usernameAndPassword = String.format("%s:%s", username, password).getBytes(StandardCharsets.UTF_8);
		String encodedUsernameAndPassword = Base64.getEncoder().encodeToString(usernameAndPassword);
		return "Basic " + encodedUsernameAndPassword;		
	}

	private boolean checkPassword(String password) {
		if(null == password || password.length() < 8)
			return false;
		boolean containsUppercase = false;
		boolean containsLowercase = false;
		boolean containsDigit = false;		
		for(char ch : password.toCharArray()) {
			if(ch >= '0' && ch <= '9')
				containsDigit = true;
			else if(ch >= 'a' && ch <= 'z')
				containsLowercase = true;
			else if(ch >= 'A' && ch <= 'Z')
				containsUppercase = true;			
		}
		return containsDigit && containsLowercase && containsUppercase;
	}	
	
}
